﻿using System;
using System.Threading;

using Microsoft.Coding4Fun.TweetCraft.Twitter;

using Moq;

using NUnit.Framework;

namespace Microsoft.Coding4Fun.TweetCraft.Tests
{
    [TestFixture]
    public class TwitterBackgroundServiceTest
    {
        private static readonly Random random = new Random(DateTime.Now.Millisecond);

        private Mock<ITwitterClient> twitterMock;
        private ITwitterBackgroundService twitterBackgroundService;

        [SetUp]
        public void SetUp()
        {
            // Initialize TwitterNet mock
            twitterMock = new Mock<ITwitterClient>();

            // Set up default expectations for ITwitterNet
            twitterMock.Setup(twitter => twitter.GetFriendsTimeline())
                .Returns(new[]
                         {
                             CreateTweet("This is a fake tweet from your friends timeline!")
                         });

            twitterMock.Setup(twitterNet => twitterNet.GetMentions())
                .Returns(new[]
                         {
                             CreateTweet("@TweetCraftTest Your first and only fake reply!")
                         });

            // Initialize Twitter background service with mock object
            twitterBackgroundService = new TwitterBackgroundService(twitterMock.Object);

            // Perform a synchronous polling
            twitterBackgroundService.Poll();
        }

        [TearDown]
        public void TearDown()
        {
            if (twitterBackgroundService != null)
                ((IDisposable)twitterBackgroundService).Dispose();
        }

        private static Tweet CreateTweet(string text)
        {
            return CreateTweet(text, 0, null, null);
        }

        [Test]
        public void TweetsTest()
        {
            // Assert that tweets are available through the background service
            Assert.IsNotNull(twitterBackgroundService.Tweets);
            Assert.IsTrue(twitterBackgroundService.Tweets.Count > 0);
        }

        [Test]
        public void RepliesTest()
        {
            // Assert that replies are available through the background service
            Assert.IsNotNull(twitterBackgroundService.Replies);
            Assert.IsTrue(twitterBackgroundService.Replies.Count > 0);
        }

        [Test]
        public void TweetsChangedTest()
        {
            // Create AutoResetEvent for signaling that the event was raised
            var tweetsChangedEvent = new AutoResetEvent(false);

            // Assert that tweets are available through the background service
            Assert.IsNotNull(twitterBackgroundService.Tweets);
            Assert.IsTrue(twitterBackgroundService.Tweets.Count > 0);

            // Get the creation date of the last tweet and reply
            DateTime recentTweetTime = twitterBackgroundService.Tweets[0].DateSent;
            DateTime recentReplyTime = twitterBackgroundService.Replies[0].DateSent;

            // Subscribe to TweetsChanged event
            twitterBackgroundService.TweetsChanged += delegate
                                                      {
                                                          // Assert that the last tweet was created later than the recent one
                                                          Assert.Less(recentTweetTime, twitterBackgroundService.Tweets[0].DateSent);

                                                          // Assert that the last reply is still created at the same time
                                                          Assert.AreEqual(recentReplyTime, twitterBackgroundService.Replies[0].DateSent);

                                                          // Signal that the event was raised
                                                          tweetsChangedEvent.Set();
                                                      };

            // Enable polling Twitter for newer tweets and set poll interval to 3 seconds
            twitterBackgroundService.PollingInterval = TimeSpan.FromSeconds(3);
            twitterBackgroundService.PollingEnabled = true;

            // Wait a little before updating the TwitterNet mock
            Thread.Sleep(TimeSpan.FromSeconds(1));

            // Update mock expectations to include the newer tweet
            twitterMock.Setup(twitter => twitter.GetFriendsTimeline())
                .Returns(new[]
                         {
                             CreateTweet("A newer tweet!")
                         });

            // Wait for the event to be raised
            if (!tweetsChangedEvent.WaitOne(TimeSpan.FromSeconds(10)))
                Assert.Fail("Timed out while waiting for TweetsChanged event to be raised.");
        }

        [Test]
        public void BackgroundPollingTest()
        {
            const string tweetMessage = "The most recent tweet.";

            // Enable polling Twitter for newer tweets
            twitterBackgroundService.PollingInterval = TimeSpan.FromSeconds(3);
            twitterBackgroundService.PollingEnabled = true;

            // Wait a little for the first poll to go through
            Thread.Sleep(TimeSpan.FromSeconds(3));

            // Assert that tweets are available through the background service
            Assert.IsNotNull(twitterBackgroundService.Tweets);
            Assert.IsTrue(twitterBackgroundService.Tweets.Count > 0);

            // Update mock expectations to include the newer tweet
            twitterMock.Setup(twitter => twitter.GetFriendsTimeline())
                .Returns(new[]
                         {
                             CreateTweet(tweetMessage)
                         });

            // Trigger an immediate async poll
            twitterBackgroundService.PollAsync();

            // Wait for the triggered poll to go through
            Thread.Sleep(twitterBackgroundService.PollingInterval);

            // Assert that the tweets have been updated
            Assert.AreEqual(tweetMessage, twitterBackgroundService.Tweets[0].Message);
        }

        private static Tweet CreateTweet(string message, int userId, string screenName, string imageUrl)
        {
            return new Tweet
                   {
                       Id = random.Next(),
                       Message = message,
                       DateSent = DateTime.Now,
                       User = new User
                                  {
                                      Id = userId,
                                      ScreenName = screenName,
                                      ImageUrl = imageUrl.ToUri()
                                  }
                   };
        }
    }
}